package com.javaspeak.designpatterns.go4.behavioural.visitor;

/**
 * This example uses the Visitor Pattern.
 * <p>
 * The output of running the example looks like:
 * <p>
 * <pre>
 * {@code
 *
 * ============================================
 * SmallShapeVisitor
 * ============================================
 *   T
 *  TTT
 * TTTTT
 *
 * xxxx
 * xxxx
 * xxxx
 *
 *
 * ============================================
 * BigShapeVisitor
 * ============================================
 *     T
 *    TTT
 *   TTTTT
 *  TTTTTTT
 * TTTTTTTTT
 *
 * Triangle
 *
 * xxxxxxxx
 * xxxxxxxx
 * xxxxxxxx
 * xxxxxxxx
 * xxxxxxxx
 *
 * Square
 *
 * }
 * </pre>
 * There are two Visitors, one is called BigShapeVisitor and the other
 * SmallShapeVisitor.  As the names imply BigShapeVisitor is responsible for
 * drawing big Shapes and SmallShapeVisitor is responsible for drawing small
 * shapes.
 * <p>
 * It make senses to centralise the code for drawing Big Shapes in
 * one place and the code for drawing small shapes in another.
 * <p>
 * <p>
 * As time goes on we may want to create new Visitors like "ShinyShapeVisitor"
 * or "3DShapeVisitor".
 * <p>
 * Instead of having to go and edit many different shape classes to produce a
 * different version of their shape a new Visitor can be created and the code
 * placed in one centralised place.
 * <p>
 * When we call the accept( ShapeVisitor shapeVisitor ) method on a
 * TriangleVisitable or SquareVisitable we pass it an implementation of
 * ShapeVisitor. Implementations include BigShapeVisitor or SmallShapeVisitor.
 * Internal to the accept method of TriangleVisitable or SquareVisitable
 * you will see the visit method is called on the Visitor passing a reference
 * of this:
 * <p>
 * <pre>
 * {@code
 *   // inside TriangleVisitable or SquareVisitable
 *
 *   public void accept( ShapeVisitor shapeVisitor ) {
 *
 *       shapeVisitor.visit( this );
 *   }
 * </pre>
 * In the Visitor you will find overloaded visit methods, one for each Visitable
 * type:
 * <pre>
 * {@code
 *   // inside BigShapeVisitor or SmallShapeVisitor
 *
 *   public void visit( TriangleVisitable triangleVisitable ) {
 *
 *        // ... code goes here to draw the big or small version of a Triangle
 *        // depending on whether this class is a BigShapeVisitor or
 *        // SmallShapeVisitor
 *   }
 *
 *   public void visit( SquareVisitable squareVisitable ) {
 *
 *       // ... code here to draw big or small version of a Square
 *       // depending on whether this class is a BigShapeVisitor or
 *       // SmallShapeVisitor
 *   }
 * </pre>
 * SquareVisitable and TriangleVisitable are Visitables.  These classes may
 * hold information useful for all Visitors. This information may be different
 * between between the Visitables as it can be specific to the Visitable
 * itself.  For example the TriangleVisitable has  a method "getCharacter()"
 * which allows the BigShapeVisitor and SmallShapeVisitor to know what character
 * to draw the triangle with.  Note that SquareVisitable does not have a
 * "getCharacter()" method but instead has a personalized "getTitle()" method.
 * <p>
 * @author John Dickerson
 */
public class ApplicationVisitor {

    /**
     * Runs example
     */
    private void runExample() {

        ShapeVisitor[] shapeVisitors =
            new ShapeVisitor[]{
                new SmallShapeVisitor(), new BigShapeVisitor() };

        ShapeVisitable[] shapeVisitables =
            new ShapeVisitable[] {
                new TriangleVisitable(), new SquareVisitable() };

        for ( ShapeVisitor shapeVisitor : shapeVisitors ){

            System.out.println( "============================================");
            System.out.println( shapeVisitor.getName() );
            System.out.println( "============================================");

            for ( ShapeVisitable shapeVisitable : shapeVisitables ){

                shapeVisitable.accept( shapeVisitor );
            }
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {

        ApplicationVisitor application = new ApplicationVisitor();
        application.runExample();
    }
}
